package com.bellszhu.elasticsearch.plugin.synonym.analysis;

import com.bellszhu.elasticsearch.plugin.DynamicSynonymPlugin;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.synonym.SynonymMap;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.env.Environment;

import java.io.*;
import java.nio.file.Path;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Properties;

/**
 * 加载MySql远程同义词
 * @author huangjiayao
 */
public class MySqlRemoteSynonymFile implements SynonymFile{

    /**
     * 数据库配置文件名
     */
    private final static String DB_PROPERTIES = "jdbc-reload.properties";
    private static Logger logger = LogManager.getLogger("dynamic-synonym");

    private String format;

    private boolean expand;

    private boolean lenient;

    private Analyzer analyzer;

    private Environment env;

    // 数据库配置
    private String location;

    /**
     * 数据库地址
     */
    private static final String JDBC_URL = "jdbc.url";
    /**
     * 数据库驱动
     */
    private static final String JDBC_DRIVER = "jdbc.driver";
    /**
     * 数据库用户名
     */
    private static final String JDBC_USER = "jdbc.user";
    /**
     * 数据库密码
     */
    private static final String JDBC_PASSWORD = "jdbc.password";

    /**
     * 当前节点的同义词版本号
     */
    private LocalDateTime thisSynonymVersion = LocalDateTime.now();

    private static Connection connection = null;

    private Statement statement = null;

    private Properties props;

    private Path conf_dir;

    MySqlRemoteSynonymFile(Environment env, Analyzer analyzer,
                           boolean expand, boolean lenient, String format, String location) {
        this.analyzer = analyzer;
        this.expand = expand;
        this.format = format;
        this.lenient = lenient;
        this.env = env;
        this.location = location;
        this.props = new Properties();

        //读取当前 jar 包存放的路径
        Path filePath = PathUtils.get(new File(DynamicSynonymPlugin.class.getProtectionDomain().getCodeSource()
                .getLocation().getPath())
                .getParent(), "config")
                .toAbsolutePath();
        this.conf_dir = filePath.resolve(DB_PROPERTIES);

        //判断文件是否存在
        File configFile = conf_dir.toFile();
        InputStream input = null;
        try {
            input = new FileInputStream(configFile);
        } catch (FileNotFoundException e) {
            logger.info("jdbc-reload.properties 数据库配置文件没有找到， " + e);
        }
        if (input != null) {
            try {
                props.load(input);
            } catch (IOException e) {
                logger.error("数据库配置文件 jdbc-reload.properties 加载失败，" + e);
            }
        }
        isNeedReloadSynonymMap();
    }

    /**
     * 加载同义词词典至SynonymMap中
     * @return SynonymMap
     */
    @Override
    public SynonymMap reloadSynonymMap() {
        try {
            logger.info("start reload local synonym from {}.", location);
            Reader rulesReader = getReader();
            SynonymMap.Builder parser = RemoteSynonymFile.getSynonymParser(rulesReader, format, expand, lenient, analyzer);
            return parser.build();
        } catch (Exception e) {
            logger.error("reload local synonym {} error! cause: {}", location, e.getMessage());
            throw new IllegalArgumentException(
                    "could not reload local synonyms file to build synonyms", e);
        }
    }

    /**
     * 判断是否需要进行重新加载
     * @return true or false
     */
    @Override
    public boolean isNeedReloadSynonymMap() {
        try {
            LocalDateTime mysqlLastModify = getMySqlSynonymLastModify();
            if (!thisSynonymVersion.isEqual(mysqlLastModify)) {
                thisSynonymVersion = mysqlLastModify;
                return true;
            }
        } catch (Exception e) {
            logger.error(e);
        }
        return false;
    }

    /**
     * 获取MySql中同义词版本号信息
     * 用于判断同义词是否需要进行重新加载
     *
     * @return getLastModify
     */
    public LocalDateTime getMySqlSynonymLastModify() {
        ResultSet resultSet = null;
        LocalDateTime mysqlSynonymLastModify = null;
        try {
            if (statement == null) {
                statement = getConnection(props);
            }
            resultSet = statement.executeQuery(props.getProperty("jdbc.reload.swith.synonym.last_modify"));
            while (resultSet.next()) {
                Timestamp lastModify = resultSet.getTimestamp("last_modify");
                mysqlSynonymLastModify = lastModify.toLocalDateTime();
                // logger.info("当前MySql同义词最后修改时间为:{}, 当前节点同义词库最后修改时间为:{}", mysqlSynonymLastModify, thisSynonymVersion);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return mysqlSynonymLastModify;
    }

    /**
     * 查询数据库中的同义词
     * @return DBData
     */
    public ArrayList<String> getDbData() {
        ArrayList<String> arrayList = new ArrayList<>();
        ResultSet resultSet = null;
        try {
            if (statement == null) {
                statement = getConnection(props);
            }
            logger.info("正在执行SQL查询同义词列表，SQL:{}", props.getProperty("jdbc.reload.synonym.sql"));
            resultSet = statement.executeQuery(props.getProperty("jdbc.reload.synonym.sql"));
            while (resultSet.next()) {
                String theWord = resultSet.getString("words");
                arrayList.add(theWord);
            }
        } catch (SQLException e) {
            logger.error(e);
        } finally {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

        }
        return arrayList;
    }

    /**
     * 同义词库的加载
     * @return Reader
     */
    @Override
    public Reader getReader() {

        StringBuilder sb = new StringBuilder();
        try {
            ArrayList<String> dbData = getDbData();
            for (String dbDatum : dbData) {
                logger.info("正在加载同义词:{}", dbDatum);
                // 获取一行一行的记录，每一条记录都包含多个词，形成一个词组，词与词之间使用英文逗号分割
                sb.append(dbDatum)
                        .append(System.getProperty("line.separator"));
            }
        } catch (Exception e) {
            logger.error("同义词加载失败");
        }
        return new StringReader(sb.toString());
    }

    /**
     * 获取数据库可执行连接
     * @param props 配置文件
     * @throws SQLException 获取连接失败
     */
    private static Statement getConnection(Properties props) throws SQLException {
        try {
            Class.forName(props.getProperty(JDBC_DRIVER));
        } catch (ClassNotFoundException e) {
            logger.error("驱动加载失败", e);
        }
        if (connection == null) {
            connection = DriverManager.getConnection(
                    props.getProperty(JDBC_URL),
                    props.getProperty(JDBC_USER),
                    props.getProperty(JDBC_PASSWORD));
        }
        return connection.createStatement();
    }
}
